package vision;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import scene.Facette3D;
import scene.Scene;

import geometrie.Point;
import geometrie.Transformation;

/**
 * Class representing a camera. This class is responsible for changing the scene
 * referential from the RefScene to RefCam. It's also responsible for the
 * projection 3D/2D. We consider here two types of camera: parallel (also known
 * as orthographic) and perspective.
 * 
 */
public class Camera {
	private Transformation refCamera;
	private Transformation projParallel;
	private Transformation projPerspective;
	private Transformation currentProj;

	/**
	 * Constructor for a new camera.
	 * 
	 * @param focus
	 *            The focus to be used for the perspective camera
	 */
	public Camera(double focus) {
		double[][] newTransformation = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 },
				{ 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
		refCamera = new Transformation(newTransformation);

		double[][] projParallelMatrix = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 },
				{ 0, 0, 0, 0 }, { 0, 0, 0, 1 } };
		projParallel = new Transformation(projParallelMatrix);
		double[][] projPerspectiveMatrix = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 },
				{ 0, 0, 1, 0 }, { 0, 0, 1 / focus, 0 } };
		projPerspective = new Transformation(projPerspectiveMatrix);
		setToParallel();
	}

	/**
	 * Change the camera current projection to parallel
	 */
	public void setToParallel() {
		currentProj = projParallel;
	}

	/**
	 * Change the Transformation responsible to change the scene's referential
	 * from RefScene to RefCam.
	 * 
	 * @param t
	 *            The new transformation
	 */
	public void setRefCamera(Transformation t) {
		refCamera = t;
	}

	/**
	 * Change the 3D/2D projection to perspective.
	 * 
	 * @param focus
	 *            The camera's focus in the perspective mode.
	 */
	public void setToPerspective(double focus) {
		double[][] projPerspectiveMatrix = { { 1, 0, 0, 0 }, { 0, 1, 0, 0 },
				{ 0, 0, 1, 0 }, { 0, 0, 1 / focus, 0 } };
		projPerspective = new Transformation(projPerspectiveMatrix);
		currentProj = projPerspective;
	}

	private List<Facette3D> applyTransformation(Transformation t,
			List<Facette3D> scene) {
		List<Facette3D> newScene = new ArrayList<Facette3D>();
		Iterator<Facette3D> facetteIterator = scene.iterator();
		Facette3D Facette;
		Facette3D newFacette;
		Point p1, p2, p3;
		while (facetteIterator.hasNext()) {
			Facette = facetteIterator.next();
			p1 = new Point(Facette.getVertex(1).getXCartesien(), Facette
					.getVertex(1).getYCartesien(), Facette.getVertex(1)
					.getZCartesien());
			p2 = new Point(Facette.getVertex(2).getXCartesien(), Facette
					.getVertex(2).getYCartesien(), Facette.getVertex(2)
					.getZCartesien());
			p3 = new Point(Facette.getVertex(3).getXCartesien(), Facette
					.getVertex(3).getYCartesien(), Facette.getVertex(3)
					.getZCartesien());
			newFacette = new Facette3D(p1, p2, p3);
			newFacette.applyTransformation(t);
			newScene.add(newFacette);
		}
		return newScene;
	}

	/**
	 * Project the scene(from refCam)
	 * 
	 * @param modifiedScene
	 * @return
	 */
	public List<Facette3D> project(List<Facette3D> modifiedScene) {
		return applyTransformation(currentProj, modifiedScene);
	}

	/**
	 * Change the referential of a scene from the originalScene to the
	 * refCamera.
	 * 
	 * @param originalScene
	 *            The original scene
	 * @return A list of the Facette3D composing the scene in which the
	 *         transformation was applied
	 */
	public List<Facette3D> changeReferential(Scene originalScene) {
		return applyTransformation(refCamera, originalScene.getFacettes());
	}
}